package com.mixide.ui.part.memory;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;

import com.mixal.hardware.Memory;
import com.mixal.software.Instruction.Opcode;

public class MemoryTableViewer {
	
	private Table table;
	private TableViewer tableViewer;
	private final Memory memory;
	private final ITableLabelProvider labelProvider;

	// Set the table column property names
	private final String NO_COLUMN = "no";
	private final String SIGN_COLUMN = "sign";
	private final String BYTE1_COLUMN = "byte1";
	private final String BYTE2_COLUMN = "byte2";
	private final String BYTE3_COLUMN = "byte3";
	private final String BYTE4_COLUMN = "byte4";
	private final String BYTE5_COLUMN = "byte5";
	private final String OPERATION_COLUMN = "operation";
	private final String TO_CHAR_COLUMN = "toChar";
	private final String TO_INT_COLUMN = "toInt";

	// Set column names
	private String[] columnNames = new String[] { 
			NO_COLUMN, 
			SIGN_COLUMN,
			BYTE1_COLUMN,
			BYTE2_COLUMN,
			BYTE3_COLUMN,
			BYTE4_COLUMN,
			BYTE5_COLUMN,
			OPERATION_COLUMN,
			TO_CHAR_COLUMN,
			TO_INT_COLUMN
	};

	public MemoryTableViewer(Composite parent, Memory memory, ITableLabelProvider labelProvider) {
		this.memory = memory;
		this.labelProvider = labelProvider;
		this.addChildControls(parent);
	}

	public void dispose() {
		tableViewer.getLabelProvider().dispose();
	}

	private void addChildControls(Composite composite) {
		createTable(composite);

		createTableViewer();
		
		tableViewer.setContentProvider(new MemoryContentProvider());
		tableViewer.setLabelProvider(this.labelProvider);
		tableViewer.setCellModifier(new MemoryCellModifier(tableViewer));
		tableViewer.setInput(this.memory);
	}

	private void createTable(Composite parent) {
		int style = SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | 
					SWT.FULL_SELECTION | SWT.HIDE_SELECTION;

		table = new Table(parent, style);
		
		GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
		data.heightHint = 200;
		table.setLayoutData(data);
					
		table.setLinesVisible(true);
		table.setHeaderVisible(true);
		
		for (int i = 0; i < this.columnNames.length; i++) {
			TableColumn column = new TableColumn(table, SWT.CENTER, i);	
			column.setText(this.columnNames[i]);
			column.setWidth(50);
			if (i == 0) {
				column.setWidth(60);
			} else if (i == 1) {
				column.setWidth(40);
			} else if (i == 7 || i == 8) {
				column.setWidth(60);
			} else if (i == 9) {
				column.setWidth(80);
			}
		}
	}

	private void createTableViewer() {

		tableViewer = new TableViewer(table);
		tableViewer.setUseHashlookup(true);
		
		tableViewer.setColumnProperties(columnNames);

		CellEditor[] editors = new CellEditor[columnNames.length];
		for (int i = 1; i < this.columnNames.length; i++) {
			editors[i] = new TextCellEditor(table);
		}
		editors[1] = new ComboBoxCellEditor(table, new String[] { "+", "-" }, SWT.READ_ONLY);
		
		editors[7] = new ComboBoxCellEditor(table, this.ops().toArray(new String[0]), SWT.READ_ONLY);
		
		tableViewer.setCellEditors(editors);
	}

	class MemoryContentProvider implements IStructuredContentProvider {
		
		public Object[] getElements(Object parent) {
			if (!(parent instanceof Memory)) {
				return null;
			}
			Memory memory = (Memory) parent;
			WordRow[] rows = new WordRow[4000];
			for (int i = 0; i < rows.length; i++) {
				rows[i] = new WordRow(memory, i);
			}
			return rows;
		}
	}

	public List<String> getColumnNames() {
		return Arrays.asList(this.columnNames);
	}
	
	private List<String> ops() {
		return Stream.of(Opcode.values()).map(Opcode::name).collect(Collectors.toList());
	}
}